package encoder;

import java.io.*;
import java.util.ArrayList;

import arithmetic.ArithmeticModel;
import arithmetic.BitOutputStream;


public class ArithmeticEncoder {
	
	private ArithmeticModel model;
	private String fPath;
	private int range;
	private int upper;
	private int lower = 0;
	private int scale3 = 0;
	
	public ArithmeticEncoder(String input) throws IOException{
		this.model = new ArithmeticModel();
		this.range = model.getRange();
		this.upper = range - 1;
		
		encode(input);
	}
	
	
	public ArithmeticEncoder(FileInputStream inStream,String filePath) throws IOException{
		BufferedReader input = new BufferedReader( new InputStreamReader(inStream) );
		
		StringBuilder sb = new StringBuilder();
		char ch;
		while((ch = (char)input.read()) !=65535  ){
			sb.append(ch);
		}
		//will provide file path for new compressed file
		this.fPath=filePath;
		
		this.model = new ArithmeticModel();
		this.range = model.getRange();
		this.upper = range - 1;
		
		encode(sb.toString());	
	}
	
	
	public void encode(String input) throws IOException{
		int i;
		int coef;
		char ch;
		char[] inputArr = (input+'\0').toCharArray();
		
		BitOutputStream output = new BitOutputStream(fPath+"compressed.bin");
		
		ArrayList<Integer> outputArr = new ArrayList<Integer>();
		
		for(i = 0; i < inputArr.length; i++){
			ch = inputArr[i];
			coef = upper - lower + 1;
			upper = lower + (int)Math.floor(coef * ((double)model.getLetterCumCount(ch) / model.getTotalCount())) - 1;
			lower = lower + (int)Math.floor(coef * ((double)model.getPreviousLetterCumCount(ch) / model.getTotalCount()));
			
			while(true){
				
				if(lower >= range/2){
					outputArr.add(1);
					output.write(1, 1);
					upper = 2*upper - range + 1;
					lower = 2*lower - range;
					
					while(scale3 > 0){
						outputArr.add(0);
						output.write(1, 0);
						scale3--;
					}
				}
				else if(upper < range/2){
					outputArr.add(0);
					output.write(1, 0);
					upper = 2*upper + 1;
					lower = 2*lower;
					
					while(scale3 > 0){
						outputArr.add(1);
						output.write(1, 1);
						scale3--;
					}
				}
				else if(lower >= range/4 && upper < 3*range/4){
					upper = 2*upper - range/2 + 1;
					lower = 2*lower - range/2;
					
					scale3++;
				}
				else
					break;
			}
		}
		
		if(lower >= range/2){
			outputArr.add(1);
			output.write(1, 1);
			while(scale3 > 0){
				outputArr.add(0);
				output.write(1, 0);
				scale3--;
			}
		}
		else{
			outputArr.add(0);
			output.write(1, 0);
			while(scale3 > 0){
				outputArr.add(1);
				output.write(1, 1);
				scale3--;
			}
			
		}
		
		StringBuilder sb = new StringBuilder();
		
		for (Integer bit : outputArr){
			sb.append(bit);
		}
		
		//store lower
		output.write(model.getBitLength()-1, lower);
		output.close();
		
		BufferedWriter fileOut = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(fPath+"BitSequens.txt")));
		fileOut.write(sb.toString());
		fileOut.close();
		
	}
	
	
}